package utils

import (
	"errors"
	"fmt"
	"github.com/dop251/goja"
	"sync"
	"time"
)

func closeStateChan(state chan int) {
	if <-state == 0 {
		state <- 1
	}
	close(state)
}

type GojaJsEngine struct {
	vmPool   sync.Pool
	jsScript string
}

// NewGojaJsEngine 创建一个新的js引擎实例
func NewGojaJsEngine(jsScript string, path string, vars map[string]interface{}) *GojaJsEngine {
	//defaultAntsPool, _ := ants.NewPool(config.MaxTaskPool)
	jsEngine := &GojaJsEngine{
		vmPool: sync.Pool{
			New: func() interface{} {

				// atomic.AddInt64(&vmNum, 1)
				// config.Logger.Printf("create new js vm%d", vmNum)
				vm := goja.New()

				// 注册本地插件
				Registry.ModuleLoader(vm, path)

				for k, v := range vars {
					if err := vm.Set(k, v); err != nil {
						// config.Logger.Printf("set variable error,err:" + err.Error())
						//return nil, errors.New("set variable error,err:" + err.Error())
						panic(errors.New("set variable error,err:" + err.Error()))
					}
				}
				state := make(chan int, 1)
				state <- 0
				time.AfterFunc(time.Millisecond*1000*60, func() {
					if <-state == 0 {
						state <- 2
						vm.Interrupt("execution timeout")
					}
				})

				_, err := vm.RunString(jsScript)
				// 超过时间也会执行到这里，如果没有超过时间，那么取出的是0，否则取出的是
				closeStateChan(state)

				if err != nil {
					//return nil, errors.New("js vm error,err:" + err.Error())
					//config.Logger.Printf("js vm error,err:" + err.Error())
					panic(errors.New("js vm error,err:" + err.Error()))
				}

				return vm
			},
		},
		jsScript: jsScript,
	}

	return jsEngine
}

func (g *GojaJsEngine) ToValue(v interface{}) goja.Value {
	vm := g.vmPool.Get().(*goja.Runtime)
	var params goja.Value
	params = vm.ToValue(v)
	g.vmPool.Put(vm)
	return params
}

func (g *GojaJsEngine) RunString(str string) (out interface{}, err error) {
	vm := g.vmPool.Get().(*goja.Runtime)
	res, err := vm.RunString(str)
	g.vmPool.Put(vm)
	return res, err
}

func (g *GojaJsEngine) Execute(functionName string, argumentList ...interface{}) (out interface{}, err error) {
	defer func() {
		if caught := recover(); caught != nil {
			err = fmt.Errorf("%s", caught)
		}
	}()

	vm := g.vmPool.Get().(*goja.Runtime)
	state := make(chan int, 1)
	state <- 0
	time.AfterFunc(time.Millisecond*1000*60, func() {
		if <-state == 0 {
			state <- 2
			vm.Interrupt("execution timeout")
		}
	})

	f, ok := goja.AssertFunction(vm.Get(functionName))
	if !ok {
		return nil, errors.New(functionName + "is not a function")
	}
	var params []goja.Value
	for _, v := range argumentList {
		params = append(params, vm.ToValue(v))
	}
	res, err := f(goja.Undefined(), params...)

	// 超过时间也会执行到这里，如果没有超过时间，那么取出的是0，否则取出的是2
	closeStateChan(state)
	//放回对象池
	g.vmPool.Put(vm)
	if err != nil {
		return nil, err
	}
	return res, err
	//return res.Export(), err
}

func (g *GojaJsEngine) Stop() {
}
